﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel;
using Medianamik.Core.Logging;
using Medianamik.Core;

namespace Medianamik.UI.ImportExportManagement
{
    public abstract class DataImporter<TType, TTypeProperty, TRecord, TRecordProperty>
    {
        #region Fields


        #endregion

        #region .ctors

        internal DataImporter(DataSource<TType, TTypeProperty, TRecord, TRecordProperty> dataSource)
        {
            _dataSource = dataSource;
        }

        #endregion

        #region Events

        public event EventHandler<RecordTypeImportingEventArgs<TType>> RecordTypeImporting;
        protected virtual bool OnRecordTypeImporting(TType importedType, int importedTypeIndex,
            int importedTypesCount)
        {
            if (RecordTypeImporting != null)
            {
                RecordTypeImportingEventArgs<TType> e = new RecordTypeImportingEventArgs<TType>(importedType, importedTypeIndex, importedTypesCount);
                RecordTypeImporting(this, e);
                if (e.Cancel)
                {
                    OnRecordTypesImported(importedTypeIndex, importedTypesCount, true);
                    return e.Cancel;
                }
            }

            return false;
        }

        public event EventHandler<RecordTypePropertyImportingEventArgs<TTypeProperty>> RecordTypePropertyImporting;
        protected virtual bool OnRecordTypePropertyImporting(TTypeProperty importedProperty, int importedPropertyIndex,
            int importedPropertiesCount)
        {
            if (RecordTypePropertyImporting != null)
            {
                RecordTypePropertyImportingEventArgs<TTypeProperty> e = new RecordTypePropertyImportingEventArgs<TTypeProperty>(importedProperty, importedPropertyIndex, importedPropertiesCount);
                RecordTypePropertyImporting(this, e);
                if (e.Cancel)
                {
                    OnRecordTypesImported(importedPropertyIndex, importedPropertiesCount, true);
                    return e.Cancel;
                }
            }

            return false;
        }

        public event EventHandler<RecordTypesImportedEventArgs> RecordTypesImported;
        protected virtual void OnRecordTypesImported(int recordTypesCount,
            int importedTypesCount, bool canceled)
        {
            if (RecordTypesImported != null)
            {
                RecordTypesImportedEventArgs e = new RecordTypesImportedEventArgs(recordTypesCount, importedTypesCount, canceled);
                RecordTypesImported(this, e);
            }
        }

        public event EventHandler<RecordTypesImportingEventArgs<TType>> RecordTypesImporting;
        protected virtual bool OnRecordTypesImporting(IList<TType> importedTypes)
        {
            if (RecordTypesImporting != null)
            {
                RecordTypesImportingEventArgs<TType> e = new
                    RecordTypesImportingEventArgs<TType>(importedTypes);
                RecordTypesImporting(this, e);
                return e.Cancel;
            }

            return false;
        }

        public event EventHandler<RecordTypePropertiesImportingEventArgs<TType, TTypeProperty>> RecordTypePropertiesImporting;
        protected virtual bool OnRecordTypePropertiesImporting(TType importedType, IList<TTypeProperty> importedTypeProperties)
        {
            if (RecordTypePropertiesImporting != null)
            {
                RecordTypePropertiesImportingEventArgs<TType, TTypeProperty> e = new
                    RecordTypePropertiesImportingEventArgs<TType, TTypeProperty>(importedType, importedTypeProperties);
                RecordTypePropertiesImporting(this, e);
                return e.Cancel;
            }

            return false;
        }

        public event EventHandler<RecordTypePropertyImportedEventArgs> RecordTypePropertyImported;
        protected virtual void OnRecordTypePropertyImported(RecordTypeProperty recordTypeProperty)
        {
            if (RecordTypePropertyImported != null)
            {
                RecordTypePropertyImportedEventArgs e = new
                    RecordTypePropertyImportedEventArgs(recordTypeProperty);
                RecordTypePropertyImported(this, e);
            }
        }

        public event EventHandler<RecordTypesImportFailedEventArgs> RecordTypesImportFailed;
        protected virtual void OnRecordTypesImportFailed(string message)
        {
            if (RecordTypesImportFailed != null)
            {
                RecordTypesImportFailedEventArgs e = new RecordTypesImportFailedEventArgs(message);
                RecordTypesImportFailed(this, e);
            }
        }

        public event EventHandler<RecordTypeImportFailedEventArgs<TType>> RecordTypeImportFailed;
        protected virtual bool OnRecordTypeImportFailed(TType importedType, int importedTypeIndex,
            int importedTypesCount, string message)
        {
            if (RecordTypeImportFailed != null)
            {
                RecordTypeImportFailedEventArgs<TType> e = new RecordTypeImportFailedEventArgs<TType>(importedType, importedTypeIndex, importedTypesCount, message);
                RecordTypeImportFailed(this, e);
                if (e.Cancel)
                {
                    OnRecordTypesImported(importedTypeIndex, importedTypesCount, true);
                    return e.Cancel;
                }
            }

            return false;
        }

        public event EventHandler<RecordTypePropertyImportFailedEventArgs<TTypeProperty>> RecordTypePropertyImportFailed;
        protected virtual bool OnRecordTypePropertyImportFailed(TTypeProperty importedProperty, int importedPropertyIndex,
            int importedPropertiesCount, string message)
        {
            if (RecordTypeImportFailed != null)
            {
                RecordTypePropertyImportFailedEventArgs<TTypeProperty> e =
                    new RecordTypePropertyImportFailedEventArgs<TTypeProperty>(importedProperty,
                        importedPropertyIndex, importedPropertiesCount, message);
                RecordTypePropertyImportFailed(this, e);
                if (e.Cancel)
                {
                    return e.Cancel;
                }
            }

            return false;
        }

        #endregion

        #region Properties

        public IEnumerable<RecordType> RecordTypes
        {
            get { return _recordTypes.AsEnumerable(); }
        }
        List<RecordType> _recordTypes = new List<RecordType>();

        protected static ILogger Logger
        {
            get
            {
                return _logger
                    ?? (_logger = new ObjectFactory<ILogger>().Get("ImportExportLogger"));
            }
        }
        private static ILogger _logger;

        protected DataSource<TType, TTypeProperty, TRecord, TRecordProperty> DataSource
        {
            get { return _dataSource; }
        }
        DataSource<TType, TTypeProperty, TRecord, TRecordProperty> _dataSource;

        #endregion

        #region Methods

        public void ImportRecordTypes()
        {
            try
            {
                List<TType> importedTypes = new List<TType>(DataSource.GetRecordTypes());

                if (!OnRecordTypesImporting(importedTypes))
                {
                    for (int i = 0; i < importedTypes.Count; i++)
                    {
                        TType importedType = importedTypes[i];

                        if (!OnRecordTypeImporting(importedType, i, importedTypes.Count))
                        {
                            //TODO : Ajouter validations supplémentaires (exemple : doublons)
                            string importedTypeName = GetImportedTypeName(importedType);

                            if (String.IsNullOrEmpty(importedTypeName))
                            {
                                if (OnRecordTypeImportFailed(importedType, i, importedTypes.Count, "Record type name cannot be null or empty."))
                                    break;
                            }
                            else
                            {
                                RecordType recordType = new RecordType(GetImportedTypeName(importedType),
                                GetImportedTypeId(importedType));

                                List<TTypeProperty> importedProperties = new List<TTypeProperty>(DataSource.GetRecordTypePropertiesByType(importedType));
                                List<RecordTypeProperty> recordProperties = new List<RecordTypeProperty>();

                                if (!OnRecordTypePropertiesImporting(importedType, importedProperties))
                                {
                                    for (int j = 0; j < importedProperties.Count; j++)
                                    {
                                        TTypeProperty importedProperty = importedProperties[j];

                                        if (!OnRecordTypePropertyImporting(importedProperty, j, importedProperties.Count))
                                        {
                                            //TODO : Ajouter validations supplémentaires (exemple : doublons)
                                            string importedTypePropertyName = GetImportedTypePropertyName(importedProperty);

                                            if (String.IsNullOrEmpty(importedTypePropertyName))
                                            {
                                                if (OnRecordTypePropertyImportFailed(importedProperty, j, 
                                                    importedProperties.Count, "Record type property name cannot be null or empty."))
                                                    break;
                                            }
                                            else
                                            {
                                                SQLDataType sqlDataType = GetImportedTypePropertySQLDataType(importedProperty);

                                                if (sqlDataType == null)
                                                {
                                                    if (OnRecordTypePropertyImportFailed(importedProperty, j, 
                                                    importedProperties.Count, "SQL Data Type of Record type property cannot be null."))
                                                    break;
                                                }
                                                else
                                                {
                                                    RecordTypeProperty recordTypeProperty = new RecordTypeProperty(recordType, importedTypePropertyName,
                                                    sqlDataType);

                                                    recordProperties.Add(recordTypeProperty);

                                                    OnRecordTypePropertyImported(recordTypeProperty);
                                                }
                                            }
                                        }
                                    }
                                }

                                if (recordProperties.Count > 0)
                                {
                                    recordProperties.ForEach(p => recordType.Properties.Add(p));
                                }
                                else
                                {
                                    //TODO : OnRecordTypeImportFailed
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception exception)
            {
                OnRecordTypesImportFailed(exception.Message);
            }
        }

        #region Abstract Members

        public abstract string GetImportedTypePropertyName(TTypeProperty property);
        public abstract string GetImportedTypeName(TType type);
        public abstract Guid? GetImportedTypePropertyId(TTypeProperty property);
        public abstract Guid? GetImportedTypeId(TType type);
        public abstract SQLDataType GetImportedTypePropertySQLDataType(TTypeProperty property);

        #endregion

        #endregion
    }

    #region EventArgs

    public class RecordTypeImportingEventArgs<T> : CancelEventArgs
    {
        public RecordTypeImportingEventArgs(T importedType, 
            int importedTypeIndex, int importedTypesCount)
        {
            _importedType = importedType;
            _importedTypeIndex = importedTypeIndex;
            _importedTypesCount = importedTypesCount;
        }

        #region Properties

        public T ImportedType
        {
            get { return _importedType; }
        }
        T _importedType;

        public int ImportedItemIndex
        {
            get { return _importedTypeIndex; }
        }
        int _importedTypeIndex;

        public int ImportedTypesCount
        {
            get { return _importedTypesCount; }
        }
        int _importedTypesCount;

        #endregion
    }

    public class RecordTypeImportFailedEventArgs<T> : CancelEventArgs
    {
        public RecordTypeImportFailedEventArgs(T importedType, 
            int importedTypeIndex, int importedTypesCount, string message)
        {
            _importedType = importedType;
            _importedTypeIndex = importedTypeIndex;
            _importedTypesCount = importedTypesCount;
            _message = message;
        }

        #region Properties

        public T ImportedType
        {
            get { return _importedType; }
        }
        T _importedType;

        public int ImportedItemIndex
        {
            get { return _importedTypeIndex; }
        }
        int _importedTypeIndex;

        public int ImportedTypesCount
        {
            get { return _importedTypesCount; }
        }
        int _importedTypesCount;

        public string Message
        {
            get { return _message; }
        }
        string _message;

        #endregion
    }

    public class RecordTypePropertyImportingEventArgs<T> : CancelEventArgs
    {
        public RecordTypePropertyImportingEventArgs(T importedProperty, 
            int importedPropertyIndex, int importedPropertiesCount)
        {
            _importedProperty = importedProperty;
            _importedPropertyIndex = importedPropertyIndex;
            _importedPropertiesCount = importedPropertiesCount;
        }

        #region Properties

        public T ImportedProperty
        {
            get { return _importedProperty; }
        }
        T _importedProperty;

        public int ImportedPropertyIndex
        {
            get { return _importedPropertyIndex; }
        }
        int _importedPropertyIndex;

        public int ImportedPropertiesCount
        {
            get { return _importedPropertiesCount; }
        }
        int _importedPropertiesCount;

        #endregion
    }

    public class RecordTypePropertyImportFailedEventArgs<T> : CancelEventArgs
    {
        public RecordTypePropertyImportFailedEventArgs(T importedProperty, 
            int importedPropertyIndex, int importedPropertiesCount, string message)
        {
            _importedProperty = importedProperty;
            _importedPropertyIndex = importedPropertyIndex;
            _importedPropertiesCount = importedPropertiesCount;
            _message = message;
        }

        #region Properties

        public T ImportedProperty
        {
            get { return _importedProperty; }
        }
        T _importedProperty;

        public int ImportedPropertyIndex
        {
            get { return _importedPropertyIndex; }
        }
        int _importedPropertyIndex;

        public int ImportedPropertiesCount
        {
            get { return _importedPropertiesCount; }
        }
        int _importedPropertiesCount;

        public string Message
        {
            get { return _message; }
        }
        string _message;

        #endregion
    }

    public class RecordTypesImportingEventArgs<T> : CancelEventArgs
    {
        public RecordTypesImportingEventArgs(IList<T> importedTypes)
        {
            _importedTypes = importedTypes;
        }

        #region Properties

        public IList<T> ImportedTypes
        {
            get { return _importedTypes; }
        }
        IList<T> _importedTypes;

        #endregion
    }

    public class RecordTypePropertiesImportingEventArgs<TType, TTypeProperty> : CancelEventArgs
    {
        public RecordTypePropertiesImportingEventArgs(TType importedType, IList<TTypeProperty> importedTypeProperties)
        {
            _importedTypeProperties = importedTypeProperties;
            _importedType = importedType;
        }

        #region Properties

        public IList<TTypeProperty> ImportedTypeProperties
        {
            get { return _importedTypeProperties; }
        }
        IList<TTypeProperty> _importedTypeProperties;

        public TType ImportedType
        {
            get { return _importedType; }
        }
        TType _importedType;

        #endregion
    }

    public class RecordTypesImportedEventArgs: EventArgs
    {
        public RecordTypesImportedEventArgs(int importedItemIndex,
            int importedTypesCount, bool canceled)
        {
            _canceled = canceled;
            _importedTypeIndex = importedItemIndex;
            _importedTypesCount = importedTypesCount;
        }

        #region Properties

        public bool Canceled
        {
            get { return _canceled; }
        }
        bool _canceled;

        public int ImportedItemIndex
        {
            get { return _importedTypeIndex; }
        }
        int _importedTypeIndex;

        public int ImportedTypesCount
        {
            get { return _importedTypesCount; }
        }
        int _importedTypesCount;

        #endregion
    }

    public class RecordTypePropertyImportedEventArgs : EventArgs
    {
        public RecordTypePropertyImportedEventArgs(RecordTypeProperty recordTypeProperty)
        {
            _recordTypeProperty = recordTypeProperty;
        }

        #region Properties

        public RecordTypeProperty RecordTypeProperty
        {
            get { return _recordTypeProperty; }
        }
        RecordTypeProperty _recordTypeProperty;

        #endregion
    }

    public class RecordTypesImportFailedEventArgs : EventArgs
    {
        public RecordTypesImportFailedEventArgs(string message)
        {
            _message = message;
        }

        public string Message
        {
            get { return _message; }
        }
        string _message;
    }

    #endregion
}
